﻿using System;
using ParserGenerator.Extensions;
using ParserGenerator.Output;
using ParserGenerator.SyntacticAnalysis;

namespace ParserGenerator.ScannerConstruction
{
    [Serializable]
    public sealed class ScannerAutomatonHtmlWriter
    {
        public IndentedWriter Writer { get; }

        public ScannerAutomatonHtmlWriter(IndentedWriter writer)
        {
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            Writer = writer;
        }

        private void WriteHtmlHeader()
        {
            Writer.Write("<!DOCTYPE html>\n");
            Writer.Write("<html>\n");
            Writer.OpenBlock();
            Writer.Write("<head>\n");
            Writer.OpenBlock();
            Writer.Write("<meta charset=\"utf-8\">\n");
            Writer.Write("<style>\n");
            Writer.OpenBlock();
            Writer.Write("a:visited { color: darkblue; }\n");
            Writer.Write("a:link    { color: darkblue; text-decoration: none; }\n");
            Writer.Write("a:hover   { color: red; font-weight: bold; }\n");
            Writer.Write("\n");
            Writer.Write("table             { border-spacing: 0; }\n");
            Writer.Write("td                { border-right: 1px solid black; border-bottom: 1px solid black; padding: 5px; }\n");
            Writer.Write("td:first-child    { border-left: 1px solid black; }\n");
            Writer.Write("tr:first-child td { border-top: 1px solid black; }\n");
            Writer.CloseBlock();
            Writer.Write("</style>\n");
            Writer.CloseBlock();
            Writer.Write("</head>\n");
        }

        private void WriteHtmlBody(ScannerAutomaton automaton)
        {
            if (automaton == null)
                throw new ArgumentNullException(nameof(automaton));
            Writer.Write("<body>\n");
            Writer.OpenBlock();
            for (int i = 0; i < automaton.States.Count; i++)
            {
                if (i > 0)
                    Writer.Write("\n");
                WriteScannerAutomatonState(automaton.States[i]);
            }
            Writer.CloseBlock();
            Writer.Write("</body>\n");
            Writer.CloseBlock();
            Writer.Write("<html>");
        }

        private void WriteScannerAutomatonState(ScannerAutomatonState state)
        {
            if (state == null)
                throw new ArgumentNullException(nameof(state));
            Writer.Write("<p><a name=\"state{0}\"><b>{0}</b></a>", state);
            if (state.Core != null)
                Writer.Write(" : {0}", state.Core);
            Writer.Write("</p>\n");
            foreach (ScannerAutomatonTransition tran in state.Transitions.Values)
            {
                Writer.Write("<p>{0}:", tran.Symbol.Number);
                Writer.Write(" {0} ->", tran.Symbol.ToString().AsHtmlText());
                Writer.Write(" <a href=\"#state{0}\">{0}</a>", tran.Target.Number);
                Writer.Write("</p>\n");
            }
            if (state.Reductions.Count > 0)
            {
                Writer.Write("<p>reductions:</p>\n");
                Writer.OpenBlock();
                foreach (LexicalRuleDeclarator rule in state.Reductions)
                {
                    Writer.Write("<p>&nbsp;&nbsp;");
                    WriteLexicalRule(rule);
                    Writer.Write("</p>\n");
                }
                Writer.CloseBlock();
            }
            if (state.Predecessors.Count > 0)
            {
                Writer.Write("<p>predecessors:</p>\n");
                Writer.Write("<p>\n");
                Writer.OpenBlock();
                foreach (ScannerAutomatonState pred in state.Predecessors)
                    Writer.Write("&nbsp;&nbsp;<a href=\"#state{0}\">{0}</a>\n", pred.Number);
                Writer.CloseBlock();
                Writer.Write("</p>\n");
            }
        }

        private void WriteLexicalRule(LexicalRuleDeclarator rule)
        {
            if (rule == null)
                throw new ArgumentNullException(nameof(rule));
            Writer.Write(rule.ToString().AsHtmlText());
        }

        public void WriteScannerAutomaton(ScannerAutomaton automaton)
        {
            if (automaton == null)
                throw new ArgumentNullException(nameof(automaton));
            WriteHtmlHeader();
            WriteHtmlBody(automaton);
        }
    }
}
